#include "udpServer.hpp"
#include "onlineUser.hpp"
#include <memory> //智能指针头文件
#include <unordered_map>
#include <fstream>
#include <signal.h>

using namespace Server;

// static void Usage(string proc)//使用手册
// {
//     cout<<"\nUsag:\n\t"<<proc<<"local_ip loacl_port\n\n";
// }
// int main(int argc ,char* argv[])
// {
//     if(argc!=3)
//     {
//         Usage(argv[0]);
//         exit(Usage_ERR);
//     }
//     uint16_t port = atoi(argv[2]);//argv是一个指针数组，里面存的是指针，指向字符串
//     string ip = argv[1];
//     std::unique_ptr<udpServer> usvr(new udpServer(port,ip));
//     usvr->initServer();
//     usvr->start();
//     return 0;
// }

const string dictTxt = "./dict.txt";
unordered_map<string, string> dict;

static bool cutString(const string &line, string *key, string *value, const string sep)
{
    auto pos = line.find(sep);
    if (pos == string::npos)
        return false;
    *key = line.substr(0, pos);
    // pos + sep.size()————我们的sep分割符，不排除是":::","  :::   ","  ###  "这些多种多样的，所以说+ sep.size()才是准确的
    *value = line.substr(pos + sep.size()); // 后面不写substr表示截取到字符串末尾
    return true;
}
static void DictPrint()
{
    for (auto &e : dict)
    {
        cout << e.first << "::::" << e.second << endl;
    }
}
static void initDict() // 小词典
{
    ifstream in(dictTxt, std::ios::binary); // 文件操作
    if (!in.is_open())
    {
        cerr << "open file error" << errno << strerror(errno) << endl;
        exit(OPEN_ERR);
    }
    string line;
    string key, value;
    while (getline(in, line))
    {
        cout << line << endl;
        if (cutString(line, &key, &value, ":"))
        {
            dict.insert(make_pair(key, value));
        }
    }
    in.close();
    cout << "load dict success" << endl;
}

// demo1
void handlerMassage(int sockfd, string clientip, uint16_t clientport, string message)
{
    // 对message进行特殊的业务逻辑处理，不关心message怎么来的——————————server通信和业务逻辑解耦！
    string response_message;
    auto iter = dict.find(message);
    if (iter == dict.end())
        response_message = "The word you searched for was not found";
    else
        response_message = iter->second;

    // 业务处理完成，开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    client.sin_port = htons(clientport);

    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

// 要实现其他业务不需要改其他的，只需要改方法就行//这就是解耦的好处！！
// demo2
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
    if (cmd.find("rm") != string::npos || cmd.find("rmdir") != string::npos || cmd.find("mv") != string::npos)
    {
        cerr << "!!!" << clientip << ":" << clientport << "#" << "用户正在做一个非法行为!" << endl;
        return;
    }
    string response;
    FILE *fp = popen(cmd.c_str(), "r"); // 读取出内容
    if (fp == nullptr)
        response = cmd + "exec error";
    // 这里就是popen成功了
    char line[gunm];
    while (fgets(line, sizeof(line), fp))
    {
        response += line;
    }
    pclose(fp);

    // 业务处理完成，开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    client.sin_port = htons(clientport);

    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

void reload(int signo) // 捕捉到2号信号之后，重新initDict();
{
    (void)signo;
    initDict();
}

// demo3
//-----------------------------------聊天室-------------------------------------
OnlineUser onlineuser;
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    if (message == "online")
        onlineuser.addUser(clientip, clientport); // 当服务端接收到“online”时,用户进入聊天室
    if (message == "offline")
        onlineuser.delUser(clientip, clientport);  // 当服务端接收到“offline”时,用户退出聊天室
    if (onlineuser.isOnline(clientip, clientport)) // 判断用户是否在线，只有在线用户才能进行聊天
    {
        // 消息的路由
        onlineuser.broadcastMessage(sockfd, message, clientip, clientport);
    }
    else // 用户没有上线，客户端提醒工作
    {
        struct sockaddr_in client;
        bzero(&client, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_addr.s_addr = inet_addr(clientip.c_str());
        client.sin_port = htons(clientport);
        string response = "你还没有上线,请先上线,执行命令:online";
        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}

//-----------------------------------聊天室-------------------------------------

// 因为udpServer.hpp里面有默认ip缺省值0.0.0.0，所以服务端就不需要bind固定的ip
// 服务端直接默认bind默认0.0.0.0或者INADDR_ANY，就可以保证任何访问我服务器8080端口的请求全部接收然后交给8080端口对应的进程处理
static void Usage(string proc) // 使用手册
{
    cout << "\nUsag:\n\t" << proc << "loacl_port\n\n";
}
int main(int argc, char *argv[]) //./udpServer loacl_port
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(Usage_ERR);
    }
    // signal(2, reload); ///////捕捉2号信号，实现简单的热加载————我们添加新数据到dict.txt里面之后，我们不需要重启服务端，只需要ctrl+c客户端，就能够成功了
    // initDict();
    //  DictPrint();

    uint16_t port = atoi(argv[1]); // argv是一个指针数组，里面存的是指针，指向字符串
    // std::unique_ptr<udpServer> usvr(new udpServer(handlerMassage, port));
    // std::unique_ptr<udpServer> usvr(new udpServer(execCommand, port)); // 这里把回调函数一改，就相当于改了上层的业务逻辑！！
    std::unique_ptr<udpServer> usvr(new udpServer(routeMessage, port)); // 这里把回调函数一改，就相当于改了上层的业务逻辑！！

    usvr->initServer();
    usvr->start();
    return 0;
}
